#include <iostream>
#include <unistd.h>
#include <stdio.h>

//  ./程序名字 or 运行命令 or 调用fork(),在操作系统角度来看,这些创建进程的方式是没有区别的

//fork()的本质就是创建子进程,与子进程相关的内核数据结构(test_struct)和进程的代码和数据,在系统里面多了一份
//***************************************

//那么问题来了,我们只是调用了fork(),创建了子进程,但是子进程对应的数据和代码以及内核数据结构怎么来的?
//默认情况下,子进程会"继承"父进程的代码和数据,内核数据结构也会以父进程为模板,初始化子进程的内核数据结构

//注意:子进程和父进程的代码是共享的,但是仅仅是fork之后的代码(虽然可以修改范围,现在暂时不用管)
//这是因为进程执行的时候有一个pc指针,时刻记录着进程运行到哪里了,fork后的子进程当然也会继承这个指针,所以fork()之前的代码不能执行,但是不代表子进程不知道fork()之前的代码,如果你愿意,你可以修改子进程的pc指针,让它执行之前的代码.
//默认情况下,子进程和父进程的数据也是共享的,这里采用引用计数的写时拷贝,如果子进程不会修改数据,那么就和父进程共享数据
//如果子进程修改数据,那么就临时将父进程的数据拷贝给子进程,如果子进程不修改数据,那么效率就会提高,如果修改数据,也和直接拷贝的效率差不多,就多了一个管理引用计数的负担
//***************************************
//
//既然子进程和父进程的代码一样,那我们创建子进程就是为了和父进程做一样的事情?
//可以通过if else进行分流,让父子做不一样的事情,可以通过fork的返回值来完成
//fork有三种返回值,父进程失败返回<0的值(-1),父进程成功返回子进程的pid,子进程返回0
//注意:如果父进程创建子进程成功这里函数会同时返回两个值

//如何理解同时有两个返回值?
//当fork()执行创建好了子进程后,还没有退出fork函数,所以这个时候子进程继承了父进程后面的代码,所以父进程会有一个返回值,子进程会有一个返回值

//fork()后的返回值是数据吗?return的时候,会进行写入吗?
//会的,到时候,会发生写实拷贝
//在修改内容之前,父子进程共享数据段和代码段,这个时候不管是虚拟地址还是物理地址都是一样的
//在修改数据之后,父子进程的代码段还是共享,虚拟和物理地址也是一样的,但是子进程的数据段会在物理内存上重现开辟,然后把父进程的数据拷贝到子进程数据段,这个时候修改子进程页表的映射关系,物理内存改变了,但是虚拟内存没有改变

//fork之后,父子谁先运行?
//这个不确定,由系统的资源调度器来决定,不过我自己测试的时候,一般情况是父进程优先运行

//*****************************************课件中的内容
//在linux中,fork函数是一个非常重要的函数,它从已存在进程中创建一个新进程
//新进程为子进程而原进程为父进程
//fork的返回值:子进程返回0,父进程返回子进程id,出错返回-1

//进程调用fork,当控制转移到内核中的fork代码后,内核做:
//分配新的内存块和内核数据结构给子进程
//将父进程部分数据结构内容拷贝至子进程
//添加子进程到系统进程列表当中
//fork返回,开始调度器调度

//fork常规用法
//1.一个父进程希望复制自己,使父子进程同时执行不同的代码段.例如,父进程等待客户端请求,生成子进程来处理请求
//2.一个进程要执行一个不同的程序.例如子进程fork返回后,调用exec函数

//fork调用失败的原因
//1.系统中有太多的进程
//2.实际用户的进程数超过了限制


int main()
{
    //对fork进行的一个测试
    std::cout << "test_son_process" << std::endl;
    pid_t id = fork();

    if (id == 0)
    {
        //child
        std::cout << "I am child, pid: " << getpid() << "    " << "My father is, ppid: " << getppid() << std::endl;
        printf("***********************::%p\n", main);
    }
    else if (id > 0)
    {
        //parent
        sleep(1);
        std::cout << "I am parent, pid: " << getpid() << "    " << "My father is, ppid: " << getppid() << std::endl;
        printf("***********************::%p\n", main);
    }
    else
    {
        perror("fork");
        return 1;
    }

    return 0;
}

